﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
using System.IO;

namespace RoardersAgentSim.Agent
{
	/// <summary>
	/// Road links manager
	/// </summary>
	public class RoadLinkManager
    {
		/// <summary>
		/// collection of road links
		/// </summary>
		private List<RoadLink> roadLinkCollection;
		/// <summary>
		/// flow to shelter s (should be the sum of q_hsk)
		/// </summary>
        public Dictionary<int, int> q_hs;
        /// <summary>
		/// Initialise the list of roadlinks from the map
		/// </summary>
		/// <param name="map"></param>
		public RoadLinkManager(Map.FullMap map)
		{
			roadLinkCollection = new List<RoadLink>();
			roadLinkCollection.AddRange(map.CarRoadLinks);
			roadLinkCollection.AddRange(map.WalkRoadLinks);
		}

		#region public methods
		/// <summary>
		/// Road links deviation
		/// </summary>
		/// <returns></returns>
		public double Deviation()
		{
			return 0;
		}
		#endregion

		/// <summary>
		/// 
		/// </summary>
		public void Initialize()
		{
			q_hs = new Dictionary<int, int>();
            LogHelper.LogStep("Road links initialized.");
		}
		#region operation methods
		/// <summary>
		/// Compute the link speed (v_kt) for all road links
		/// </summary>
		public void ComputeLinkSpeed()
		{
			LogHelper.LogStep("Computing link speed A3.1");
            string log = "Found";
			foreach (RoadLink rl in this.roadLinkCollection)
			{
				rl.ComputeLinkSpeed();
                log += " " + rl.Description + ":" + rl.v_kt.ToString();
			}
            LogHelper.LogStep(log,true);
		}
		/// <summary>
		/// Get min walk per roadlink
		/// </summary>
		/// <param name="zoneID"></param>
		/// <param name="shelterID"></param>
		/// <returns></returns>
		public double MinWalk(int zoneID, int shelterID)
		{
            double min = double.PositiveInfinity;
            var query = this.roadLinkCollection.FindAll(x => x.Link != null && x.Link.H.ZoneID == zoneID && x.g_walk.ContainsKey(shelterID));
            if (query != null)
            {
                if (query.Any(x=>x.g_walk.ContainsKey(shelterID)))
                    min = query.Min<RoadLink>(x => x.g_walk[shelterID]);
            }
            return min;
		}

		/// <summary>
		/// Get min car per roadlink
		/// </summary>
		/// <param name="zoneID"></param>
		/// <param name="shelterID"></param>
		/// <returns></returns>
		public double MinCar(int zoneID, int shelterID)
		{
            double min = double.PositiveInfinity;
			var query = this.roadLinkCollection.FindAll(x => x.Link != null && x.Link.H.ZoneID == zoneID && x.g_car.ContainsKey(shelterID));
			if (query != null)
			{
                 if (query.Any(x=>x.g_car.ContainsKey(shelterID)))
				    min = query.Min<RoadLink>(x => x.g_car[shelterID]);
			}
			return min;
		}

		#endregion
        /// <summary>
        /// 
        /// </summary>
        /// <param name="dictionary"></param>
        internal void InitializeShelterFlows(Dictionary<int, int> dictionary)
        {
            q_hs = dictionary;

            foreach (RoadLink rl in this.roadLinkCollection)
                rl.q_hsk = dictionary;
            
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="step"></param>
        internal void ExportTotalFlowState()
        {
            string directory = Utils.GetOutputDirectory();
            string totalFlowFileName = string.Format("{0}_{1}.{2}", Utils.FilePrefix,"total_flow", "csv");
            StreamWriter fileTotalFlow = new StreamWriter(directory + totalFlowFileName, true);
            if (!Utils.OutPutInitialised)
            {
                fileTotalFlow.WriteLine(GetTimeLinksHeader());
            }
            fileTotalFlow.WriteLine(GetTotalFlowRow());
            fileTotalFlow.Close();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="step"></param>
        internal void ExportMaxFlowState()
        {
            string directory = Utils.GetOutputDirectory();
            string maxFlowFileName = string.Format("{0}_{1}.{2}", Utils.FilePrefix, "max_flow", "csv");
            StreamWriter fileMaxFlow = new StreamWriter(directory + maxFlowFileName, true);
            if (!Utils.OutPutInitialised)
            {
                fileMaxFlow.WriteLine(GetTimeLinksHeader());
            }
            fileMaxFlow.WriteLine(GetMaxFlowRow());
            fileMaxFlow.Close();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="step"></param>
        internal void ExportCongestionFlowState(int step)
        {
            string directory = Utils.GetOutputDirectory();
            string filename = string.Format("{0}_{1}.{2}", Utils.FilePrefix, "congestion", "csv");
            StreamWriter file = new StreamWriter(directory + filename, true);

            file.WriteLine(GetTimeLinksHeader());

            file.WriteLine(GetCongestionRow(step));
            file.Close();
        }

        private string GetCongestionRow(int step)
        {
            string row = step.ToString();
            foreach (RoadLink rl in this.roadLinkCollection)
            {
                double congestion = rl.MaxFlow / rl.y;
                row += Utils.GetCSVDelimiter() + congestion.ToString();
            }
            return row;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="step"></param>
        /// <returns></returns>
        private string GetMaxFlowRow()
        {
            string row = null;
            foreach (RoadLink rl in this.roadLinkCollection)
            {
                if (string.IsNullOrEmpty(row))
                    row = rl.MaxFlow.ToString();
                else
                    row += Utils.GetCSVDelimiter() + rl.MaxFlow.ToString();
            }
            return row;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="step"></param>
        /// <returns></returns>
        private string GetTotalFlowRow()
        {
            string row = null;
            foreach (RoadLink rl in this.roadLinkCollection)
            {
                if(string.IsNullOrEmpty(row))
                    row = rl.TotalFlow.ToString();
                else
                    row += Utils.GetCSVDelimiter() + rl.TotalFlow.ToString();
            }
            return row;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        private string GetTimeLinksHeader()
        {
            string header = "";
            foreach (RoadLink rl in this.roadLinkCollection)
            {
                if (string.IsNullOrEmpty(header))
                    header = rl.Description;
                else
                    header += Utils.GetCSVDelimiter() + rl.Description;
            }
            return header;
        }
    }
}
